#include "../Prec.h"

CBBall::CBBall(CBWorld* world,float x,float y)
  :CBBody(world)
{
  mCircle.x = x;
  mCircle.y = y;
  mRadius = CCDefine::Instance()->BallRadius();
  mType = EBT_Ball;
  mSpeed.x = 0;
  mSpeed.y = 0;
  mForce = false;
  memset(mConnect,0,sizeof(mConnect));
  mColor = 0;
}


CBBall::~CBBall(void)
{
  for(int i=0;i<7;i++)
  {
    if(mConnect[i])
    {
      if(i==0)
      {
        CBSide* side = (CBSide*)mConnect[i];
        side->DisConnect(this);
        mConnect[i]=NULL;
      }
      else
      {
        CBBall* ball = (CBBall*)mConnect[i];
        CCAssert(ball->mConnect[7-i]==this,"");
        ball->mConnect[7-i]=NULL;
        mConnect[i]=NULL;
      }
    }
  }
}

void CBBall::Move(float deltatime,CCPoint& Force)
{
  if(mMoved)
    return ;
  
  std::list<CBBall*> tmpGroupBallLst;
  GetAllConnectedBall(tmpGroupBallLst);
  
  float nowspeed = sqrt(mSpeed.x*mSpeed.x+mSpeed.y*mSpeed.y);

  if(mForce)
  {
    int mass=0;
    for(std::list<CBBall*>::iterator iter=tmpGroupBallLst.begin();iter!=tmpGroupBallLst.end();++iter)
    {
      CBBall* ball = *iter;
      mass+=ball->GetMass();
    }
    if(mass==0)
      return ;
    float ax = Force.x/mass;
    float ay = Force.y/mass;

    float deltaX = mSpeed.x*deltatime+ax*deltatime*deltatime/2;
    float deltaY = mSpeed.y*deltatime+ay*deltatime*deltatime/2;
    float deltaAX = ax*deltatime;
    float deltaAY = ay*deltatime;
    bool bNoCollision=false;
    if(deltaX!=0||deltaY!=0||deltaAX!=0||deltaAY!=0)
      bNoCollision=true;
    for(std::list<CBBall*>::iterator iter=tmpGroupBallLst.begin();iter!=tmpGroupBallLst.end();++iter)
    {
      CBBall* ball = *iter;
      ball->mCircle.x+=deltaX;
      ball->mCircle.y+=deltaY;
      ball->mSpeed.x+=deltaAX;
      ball->mSpeed.y+=deltaAY;
      if(bNoCollision)
        ball->mNoCollision=true;
    }
  }
  else
  {
    float deltaX = mSpeed.x*deltatime;
    float deltaY = mSpeed.y*deltatime;
    bool Cross=false;
    for(std::list<CBBall*>::iterator iter=tmpGroupBallLst.begin();iter!=tmpGroupBallLst.end();++iter)
    {
      CBBall* ball = *iter;
      if(mWorld->mWidth>0)
      {
        if(ball->mCircle.x+deltaX+mRadius<0||ball->mCircle.x+deltaX-mRadius>mWorld->mWidth)
        {
          Cross=true;
          break;
        }
      }
      if(mWorld->mHeight>0)
      {
        if(ball->mCircle.y+deltaY+mRadius<0||ball->mCircle.y+deltaY-mRadius>mWorld->mHeight)
        {
          Cross=true;
          break;
        }
      }
    }
    if(Cross)
      return ;
    for(std::list<CBBall*>::iterator iter=tmpGroupBallLst.begin();iter!=tmpGroupBallLst.end();++iter)
    {
      CBBall* ball = *iter;
      ball->mCircle.x+=deltaX;
      ball->mCircle.y+=deltaY;
    }
  }
}

float CBBall::GetMass()
{
  return 1;
}

bool CBBall::CalcBodyForce(CCPoint& force)
{
  if(mCalced)
    return true;
  if(!mForce)
  {
    force.x = 0;
    force.y = 0;
    return false;
  }
  mCalced = true;
  force.x += GetMass()*mWorld->mGravity.x;
  force.y += GetMass()*mWorld->mGravity.y;
  for(int i=0;i<4;++i)
  {
    if(mConnect[i])
    {
      if(!mConnect[i]->CalcBodyForce(force))
        return false;
    }
  }
  for(int i=4;i<7;++i)
  {
    if(mConnect[i])
    {
      if(!mConnect[i]->CalcBodyForce(force))
        return false;
    }
  }
  return true;
}

void CBBall::GetAllConnectedBall(std::list<CBBall*>& Lst)
{
  if(mMoved)
    return ;
  mMoved = true;
  Lst.push_back(this);
  for(int i=1;i<4;++i)
  {
    if(mConnect[i])
    {
      CBBall* ball = (CBBall*)mConnect[i];
      ball->GetAllConnectedBall(Lst);
    }
  }
  for(int i=4;i<7;++i)
  {
    if(mConnect[i])
    {
      CBBall* ball = (CBBall*)mConnect[i];
      ball->GetAllConnectedBall(Lst);
    }
  }
}

bool CBBall::CheckCollision(CBBody* body)
{
  if(mNoCollision)
    return false;
  CBBall* curFlyBall = (CBBall*)body;

  if(CCDefine::Instance()->Distance(curFlyBall->mSpeed)==0)
    return false;

  float Dis = CCDefine::Instance()->Distance(curFlyBall->mCircle,mCircle);
  if(Dis>=curFlyBall->mRadius+mRadius)
    return false;

  CCPoint v1(mCircle.x-curFlyBall->mCircle.x,mCircle.y-curFlyBall->mCircle.y);
  if(v1.x*curFlyBall->mSpeed.x+v1.y*curFlyBall->mSpeed.y<=0)
    return false;

  return true;
}

void CBBall::Collision(CBBody* body) 
{
  if(body->mType==EBT_Side)
  {
    CBSide* side = (CBSide*)body;
    if(side->mSideType!=EST_TOP)
    {
      if(side->mPtA.x<mCircle.x)
        mSpeed.x=mSpeed.x>0?mSpeed.x:-mSpeed.x;
      else
        mSpeed.x=mSpeed.x<0?mSpeed.x:-mSpeed.x;
      return ;
    }
  }
  Connect(body);
}

void CBBall::Connect(CBBody* body)
{
  mSpeed.x=0;
  mSpeed.y=0;
  mForce=true;
  for(std::list<CBBody*>::iterator iter = mWorld->mBodyLst.begin();iter!=mWorld->mBodyLst.end();++iter)
  {
    if(this==*iter)
      continue;
    CBBody* body = *iter;
    if(body->mType==EBT_Side)
    {
      CBSide* side = (CBSide*)body;
      if(side->mSideType!=EST_TOP)
        continue;
      float dis = side->GetDistance(this);
      if(dis<mRadius+mRadius*sinf((float)M_PI_2/3))
      {
        int index= mCircle.x/mRadius/2;
        if(index>=side->mBallNum)
          index=side->mBallNum-1;
        CBBody* cnct = side->mConnect[index];
        if(!cnct)
        {
          side->mConnect[index] = this;
          mConnect[0] = side;
          mCircle.x = (side->mPtA.x<side->mPtB.x?side->mPtA.x:side->mPtB.x)+mRadius+index*mRadius*2;
          mCircle.y = side->mPtB.y-mRadius;
        }
      }
    }
    else if(body->mType==EBT_Ball)
    {
      CBBall* ball = (CBBall*)body;
      float dis = CCDefine::Instance()->Distance(mCircle,ball->mCircle);
      if(dis<(sqrt(3.0f)+1)*CCDefine::Instance()->BallRadius())
      {
        float deltax = mCircle.x-ball->mCircle.x;
        float deltay = mCircle.y-ball->mCircle.y;
        float angle = atan2f(deltay,deltax)/M_PI*180;
        angle = CCDefine::Instance()->Angle(angle);
        if(angle<=30||angle>330)
        {
          if(!ball->mConnect[5])
          {
            ball->mConnect[5] = this;
            mConnect[2] = ball;
            mCircle.x = ball->mCircle.x+2*mRadius;
            mCircle.y = ball->mCircle.y;
          }
        }
        else if(angle>30&&angle<=90)
        {
          if(!ball->mConnect[4])
          {
            ball->mConnect[4] = this;
            mConnect[3] = ball;
            mCircle.x = ball->mCircle.x+mRadius;
            mCircle.y = ball->mCircle.y+mRadius*sqrt(3.0f);
          }
        }
        else if(angle>90&&angle<=150)
        {
          if(!ball->mConnect[1])
          {
            ball->mConnect[1] = this;
            mConnect[6] = ball;
            mCircle.x = ball->mCircle.x-mRadius;
            mCircle.y = ball->mCircle.y+mRadius*sqrt(3.0f);
          }
        }
        else if(angle>150&&angle<=210)
        {
          if(!ball->mConnect[2])
          {
            ball->mConnect[2] = this;
            mConnect[5] = ball;
            mCircle.x = ball->mCircle.x-2*mRadius;
            mCircle.y = ball->mCircle.y;
          }
        }
        else if(angle>210&&angle<=270)
        {
          if(!ball->mConnect[3])
          {
            ball->mConnect[3] = this;
            mConnect[4] = ball;
            mCircle.x = ball->mCircle.x-mRadius;
            mCircle.y = ball->mCircle.y-mRadius*sqrt(3.0f);
          }
        }
        else if(angle>270&&angle<=330)
        {
          if(!ball->mConnect[6])
          {
            ball->mConnect[6] = this;
            mConnect[1] = ball;
            mCircle.x = ball->mCircle.x+mRadius;
            mCircle.y = ball->mCircle.y-mRadius*sqrt(3.0f);
          }
        }
      }
    }
  }
}

int CBBall::SameColor(int color,std::list<CCSprite*>& removelst)
{
  if(mCalced)
    return 0;
  mCalced=true;
  if(mColor!=color)
    return 0;
  assert(mShow);
  removelst.push_back(mShow);
  int num=1;
  for(int i=1;i<7;i++)
  {
    if(mConnect[i])
    {
      CBBall* ball = (CBBall*)mConnect[i];
      num+=ball->SameColor(color,removelst);
    }
  }
  return num;
}

